/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.tastefuljava.hiketools.geo;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author maurice
 */
public class Polyline {
    private int numLevels;
    private double zoomFactor;
    private double epsilon;
    private PolylinePoint points[];

    public Polyline(int numLevels, double zoomFactor, double epsilon,
            TrackPoint track[]) {
        this.numLevels = numLevels;
        this.zoomFactor = zoomFactor;
        this.epsilon = epsilon;
        List<PolylinePoint> pointList = new ArrayList<PolylinePoint>();
        if (track.length > 0) {
            pointList.add(new PolylinePoint(track[0], 0));
            if (track.length > 2) {
                douglasPeucker(track, 0, track.length-1, pointList);
                pointList.add(new PolylinePoint(track[track.length-1], 0));
            }
        }
        points = pointList.toArray(new PolylinePoint[pointList.size()]);
    }

    public Polyline(TrackPoint track[]) {
        this(4, 32, 5, track);
    }

    public int getNumLevels() {
        return numLevels;
    }

    public double getZoomFactor() {
        return zoomFactor;
    }

    public double getEpsilon() {
        return epsilon;
    }

    public int getNumPoints() {
        return points.length;
    }

    public PolylinePoint[] getPoints() {
        return points.clone();
    }

    private void douglasPeucker(TrackPoint track[], int start, int end,
            List<PolylinePoint> pointList) {
        if (end-start >= 2) {
            double max = 0;
            int index = 0;
            TrackPoint startPt = track[start];
            TrackPoint endPt = track[end];
            for (int i = start+1; i < end; ++i) {
                double dist = track[i].distanceToGreatCircle(startPt, endPt);
                if (dist > max) {
                    max = dist;
                    index = i;
                }
            }
            if (max > epsilon) {
                douglasPeucker(track, start, index, pointList);
                pointList.add(new PolylinePoint(track[index],
                        levelForDist(max)));
                douglasPeucker(track, index, end, pointList);
            }
        }
    }

    private int levelForDist(double dist) {
        int level = numLevels;
        double eps = epsilon;
        while (level > 0 && dist > eps) {
            --level;
            eps *= zoomFactor;
        }
        return level;
    }
}
